package com.bxkc.generator.service.impl;

import com.bxkc.generator.constant.VMConstant;
import com.bxkc.generator.dao.TableDAO;
import com.bxkc.generator.domain.ColumnDO;
import com.bxkc.generator.dto.GeneratorDTO;
import com.bxkc.generator.enums.ColumnDataTypeEnum;
import com.bxkc.generator.response.CommonResult;
import com.bxkc.generator.service.GeneratoService;
import com.bxkc.generator.utils.TableUtils;
import com.bxkc.generator.utils.VelocityTemplateUtils;
import com.bxkc.generator.vo.ColumnVO;
import com.bxkc.generator.vo.TableVO;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhanxiumei
 */
@Service
public class GeneratoServiceImpl implements GeneratoService {
    private List<TableVO> tableVOList = new ArrayList<>();
    /**
     * 文件输出位置
     */
    @Value("${generator.outpath}")
    private String outPath;

    @Resource
    private TableDAO tableDAO;

    /**
     * 构建代码业务接口
     *
     * @param dto
     * @return
     */
    @Override
    public CommonResult build(GeneratorDTO dto, boolean buildAll) {

        TableVO tableVO = getTableVO(dto);
        setGetPrimaryKeyColumnMethodName(tableVO);
        setSetPrimaryKeyColumnMethodName(tableVO);
        setTableAttributeName(tableVO);
        if (buildAll) {
            tableVOList.add(tableVO);
        } else {
            createSysMenuSQL(tableVO);
        }
        outFile(tableVO);
        return CommonResult.success();
    }

    @Override
    public CommonResult buildAll(GeneratorDTO dto) {
        List<String> allTableName = tableDAO.findAllTableName(dto);

        if (!CollectionUtils.isEmpty(allTableName)) {
            allTableName.parallelStream().forEach(tableName -> {
                GeneratorDTO obj = new GeneratorDTO();
                obj.setDbName(dto.getDbName());
                obj.setPackageName(dto.getPackageName());
                obj.setTableName(tableName);
                build(obj, true);
            });
        }
        List<String> sql = new ArrayList<>();
        tableVOList.stream().filter(Objects::nonNull).forEach(vo-> sql.addAll(createSysMenuSQL(vo)));
        VelocityContext context = new VelocityContext();
        context.put("sysMenuSqlList", sql);

        VelocityTemplateUtils.writerFile("template/sql/sql.vm", context, outPath+"sql/sysMenu.sql");
        return CommonResult.success();
    }

    /**
     * 构建指定的表
     *
     * @param dto
     * @return
     */
    @Override
    public CommonResult buildSpecifyTableNames(GeneratorDTO dto) {

        List<String> specifyTableNames = dto.getSpecifyTableNames();

        if (!CollectionUtils.isEmpty( specifyTableNames )) {
            specifyTableNames.parallelStream().forEach( tableName -> {
                GeneratorDTO obj = new GeneratorDTO();
                obj.setDbName( dto.getDbName() );
                obj.setPackageName( dto.getPackageName() );
                obj.setTableName( tableName );
                build( obj ,true);
            } );
        }
        List<String> sql = new ArrayList<>();
        tableVOList.forEach(vo-> sql.addAll(createSysMenuSQL(vo)));
        VelocityContext context = new VelocityContext();
        context.put("sysMenuSqlList", sql);

        VelocityTemplateUtils.writerFile("template/sql/sql.vm", context, outPath+"sql/sysMenu.sql");
        return CommonResult.success();
    }

    private List<String> createSysMenuSQL(TableVO tableVO) {
        List<String> sqlList = new ArrayList<>();

        String commonSql
            = "insert into `sys_menu` (`parent_id`,`name`,`perms`,`url`,`create_time`,`icon`,`show`,`enable`,"
            + "`remark`) values ";
        String attributeName = tableVO.getAttributeName();

        String parentSql = commonSql +"(1,'"+tableVO.getTableComment()+"',null,null,now(),null,'normal','normal','null');";

        String getParentId = "(select menu_id from (select `menu_id` from `sys_menu` where name = '"+tableVO.getTableComment()+"' limit 0,1) as a group by a.menu_id)";

        String insert = commonSql +"("+getParentId+",'新增','sys:"+attributeName+":insert',null,now(),null,'normal','normal','null');";
        String selectById = commonSql +"("+getParentId+",'根据ID查询','sys:"+attributeName+":selectById',null,now(),null,'normal','normal','null');";
        String deleteById = commonSql +"("+getParentId+",'根据ID删除','sys:"+attributeName+":deleteById',null,now(),null,'normal','normal','null');";
        String batchDeleteByIds = commonSql +"("+getParentId+",'根据ID批量删除','sys:"+attributeName+":batchDeleteByIds',null,now(),null,'normal','normal','null');";
        String updateById = commonSql +"("+getParentId+",'根据ID更新数据','sys:"+attributeName+":updateById',null,now(),null,'normal','normal','null');";
        String pageByCondition = commonSql +"("+getParentId+",'分页查询','sys:"+attributeName+":pageByCondition',null,now(),null,'normal','normal','null');";
        String findAll = commonSql +"("+getParentId+",'查询全部','sys:"+attributeName+":findAll',null,now(),null,'normal','normal','null');";


        sqlList.add(parentSql);
        sqlList.add(insert);
        sqlList.add(selectById);
        sqlList.add(deleteById);
        sqlList.add(batchDeleteByIds);
        sqlList.add(updateById);
        sqlList.add(pageByCondition);
        sqlList.add(findAll);
        tableVO.setSysMenuSql(sqlList);
        return sqlList;
    }


    /**
     * 获取主键列列名的get方法名称
     *
     * @return
     */
    private void setGetPrimaryKeyColumnMethodName(TableVO tableVO) {
        List<ColumnVO> columnList = tableVO.getColumnList();
        for (ColumnVO columnVO : columnList) {
            if (columnVO.isPrimaryKey()) {
                String columnName = columnVO.getColumnName();
                tableVO.setGetPrimaryKeyMethodName("get" + TableUtils.tableNameToClassName(columnName));
                return;
            }
        }
    }

    private void setSetPrimaryKeyColumnMethodName(TableVO tableVO) {
        List<ColumnVO> columnList = tableVO.getColumnList();
        for (ColumnVO columnVO : columnList) {
            if (columnVO.isPrimaryKey()) {
                String columnName = columnVO.getColumnName();
                tableVO.setSetPrimaryKeyMethodName("set" + TableUtils.tableNameToClassName(columnName));
                return;
            }
        }
    }

    /**
     * 获取类在方法调用中作为对象的对象名
     *
     * @return
     */
    private void setTableAttributeName(TableVO tableVO) {
        String className = TableUtils.tableNameToClassName(tableVO.getTableName());
        tableVO.setAttributeName(TableUtils.getFirstLowerString(className));
    }

    /**
     * 输出构建好的代码文件到指定文件夹
     */
    private void outFile(TableVO tableVO) {
        Map<String, String> templatePath = VMConstant.getTemplatePath();
        templatePath.forEach((k, v) -> {
            String filePath = outPath + v.replace("###", tableVO.getClassName());
            VelocityTemplateUtils.writerFile(k, getVelocityContext(tableVO), filePath);
        });
    }

    /**
     * 获取velcity参数数据
     *
     * @return
     */
    private VelocityContext getVelocityContext(TableVO tableVO) {
        VelocityContext context = new VelocityContext();
        context.put("TableVO", tableVO);
        return context;
    }

    private TableVO getTableVO(GeneratorDTO dto) {
        TableVO vo = new TableVO();
        List<ColumnVO> columnVOList = new ArrayList<>();
        Set<String> importSet = new HashSet<>();
        String primaryKeyColumnDataType = "";
        String primaryKeyColumnName = "";
        String columnName = "";
        // 当前表的全部列信息
        List<ColumnDO> columnDOList = tableDAO.getAllColumn(dto);
        for (ColumnDO column : columnDOList) {
            ColumnVO columnVO = new ColumnVO();
            columnVO.setColumnComment(column.getColumnComment());
            columnVO.setColumnName(column.getColumnName());
            ColumnDataTypeEnum dataType = ColumnDataTypeEnum.getBySqlDataType(column.getColumnDataType());
            if (dataType != null && dataType.isImport()) {
                importSet.add("import " + dataType.getJavaPackage() + ";");
            }

            columnVO.setJavaType(dataType.getJavaType());
            columnVO.setFiledName(
                TableUtils.getFirstLowerString(TableUtils.tableNameToClassName(column.getColumnName())));
            if (column.getColumnKey() != null && column.getColumnKey().contains("PRI")) {
                columnVO.setPrimaryKey(true);
                primaryKeyColumnDataType = dataType.getJavaType();
                primaryKeyColumnName = columnVO.getFiledName();
                columnName = column.getColumnName();
            } else {
                columnVO.setPrimaryKey(false);
            }
            columnVO.setJdbcType(dataType.getJdbcType().getName());
            columnVO.setMethodName(TableUtils.tableNameToClassName(columnVO.getColumnName()));
            columnVO.setExtra("auto_increment".equals(column.getExtra()));
            columnVO.setNullable("NO".equals(column.getNullable()));
            columnVO.setUpperCaseColumnName(column.getColumnName().toUpperCase());
            columnVO.setUnique("PRI".equals(column.getColumnKey()) || "UNI".equals(column.getColumnKey()));
            columnVOList.add(columnVO);
        }

        List<String> baseColumnList = columnVOList.stream().map(o -> "`" + o.getColumnName() + "`").collect(
            Collectors.toList());
        vo.setBaseColumnList(String.join(",", baseColumnList));
        String tableComment = tableDAO.selectTableComment(dto);
        vo.setImportSet(importSet);
        vo.setTableComment(tableComment);
        vo.setColumnList(columnVOList);
        vo.setTableName(dto.getTableName());
        vo.setPackageName(dto.getPackageName());
        vo.setClassName(TableUtils.tableNameToClassName(dto.getTableName()));
        vo.setPrimaryKeyColumnName(primaryKeyColumnName);
        vo.setPrimaryKeyColumnDataType(primaryKeyColumnDataType);
        vo.setColumnName( columnName );
        return vo;
    }

}
